package model.entity.container;

import model.exceptions.InvalidNameException;
import model.exceptions.NullContainerException;
import reports.visitor.IVisitor;

import common.Result;
import common.Size;

// @formatter:off
/**
 * Container to organize similar products into groups.
 * 
 * @author Trevor Dixon, Ryan Cheatham
 * 
 *         {@invariant getThreeMonthSupply() > 0} {@invariant container != null}
 */
// @formatter:on
public class ProductGroup extends ProductContainer implements Comparable<ProductGroup>
{
  private static final long serialVersionUID = 2857664814855087394L;

  private ProductContainer  container;
  private Size              threeMonthSupply;

  // @formatter:off
  /**
   * Creates a product group with the given name, container, and three month supply.
   * 
   * @param name
   *          User-defined name of the group. Must be unique among all
   *          <code>ProductContainer</code>s in the parent container.
   * 
   * @param container
   *          The <code>ProductContainer</code> that is the parent of this group.
   * 
   * @param threeMonthSupply
   *          The number of items needed to constitute a "three month supply" for this group.
   * 
   *          {@pre name != null} {@pre name.length() > 0} {@pre container != null} {@pre
   *          threeMonthSupply >= 0}
   * 
   *          {@post getName() == name} {@post getContainer() == container} {@post
   *          getThreeMonthSupply() == threeMonthSupply}
   * @throws InvalidNameException
   * @throws NullContainerException 
   */
  // @formatter:on
  public ProductGroup(String name, ProductContainer container, Size threeMonthSupply)
      throws InvalidNameException, NullContainerException
  {
    super(name);

    if (container == null)
      throw new NullContainerException("Container must not be null.");

    Result result = Size.validateProductContainerSize(threeMonthSupply);
    if (!result.getStatus())
      throw new IllegalArgumentException("Three month supply invalid.");

    this.container = container;
    setParent_id(container.getId());
    this.threeMonthSupply = threeMonthSupply;
  }

  public ProductGroup(String name, Size threeMonthSupply) throws InvalidNameException
  {
    super(name);

    Result result = Size.validateProductContainerSize(threeMonthSupply);
    if (!result.getStatus())
      throw new IllegalArgumentException("Three month supply invalid.");

    this.container = null;
    this.threeMonthSupply = threeMonthSupply;
  }

  /**
   * @return The number of items needed to constitute a "three month supply" for this group.
   */
  public Size getThreeMonthSupply()
  {
    return threeMonthSupply;
  }

  /**
   * @param the
   *          new three month supply that is going to be set
   */
  public Result setThreeMonthSupply(Size newSupply)
  {
    Result result = Size.validateSize(threeMonthSupply);
    if (!result.getStatus())
    {
      return result;
    }
    threeMonthSupply = newSupply;
    return new Result(true);
  }

  /**
   * @return The parent <code>ProductContainer</code>.
   */
  public ProductContainer getContainer()
  {
    return container;
  }

  @Override
  public StorageUnit getStorageUnit()
  {
    ProductContainer unit = container;
    while (!(unit instanceof StorageUnit))
      unit = ((ProductGroup) unit).getContainer();

    return (StorageUnit) unit;
  }

  public void updateTo(ProductGroup after)
  {
    name = after.name;
    container = after.container;
    threeMonthSupply = after.threeMonthSupply;
  }

  public void setContainer(ProductContainer pc)
  {
    container = pc;
  }

  @Override
  public int hashCode()
  {
    final int prime = 31;
    int result = super.hashCode();
    result = prime * result + ((container == null) ? 0 : container.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj)
  {
    if (this == obj)
      return true;
    if (!super.equals(obj))
      return false;
    if (!(obj instanceof ProductGroup))
      return false;
    ProductGroup other = (ProductGroup) obj;
    if (container == null)
    {
      if (other.container != null)
        return false;
    }
    else if (!container.equals(other.container))
      return false;
    return true;
  }

  @Override
  public int compareTo(ProductGroup o)
  {
    int result = name.compareToIgnoreCase(o.name);
    if (result == 0)
    {
      if (container instanceof ProductGroup && o.container instanceof ProductGroup)
        result = ((ProductGroup) container).compareTo((ProductGroup) o.container);
      else if (container instanceof StorageUnit && o.container instanceof StorageUnit)
        result = ((StorageUnit) container).compareTo((StorageUnit) o.container);
      else if (container instanceof ProductGroup)
        result = 1;
      else
        result = -1;
    }

    return result;
  }

  @Override
  public void preOrderAccept(IVisitor visitor)
  {
    visitor.visit(this);
  }

  @Override
  public void postOrderAccept(IVisitor visitor)
  {
    visitor.visit(this);
  }

  @Override
  public String toString()
  {
    StringBuilder builder = new StringBuilder();
    builder.append("ProductGroup [name=");
    builder.append(name);
    builder.append(", container=");
    builder.append(container);
    builder.append(", threeMonthSupply=");
    builder.append(threeMonthSupply);
    builder.append("]");
    return builder.toString();
  }
}
